package com.zbkj.common.acpect;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.beust.jcommander.internal.Lists;
import com.zbkj.common.annotation.Cache;
import com.zbkj.common.utils.RedisUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Component
public class CacheAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private RedisUtil redisUtil;

    public CacheAspect() {
    }

    public static String getMethodLongName(Method method) {
        StringBuilder sb = new StringBuilder();
        sb.append(method.getName()).append("(");
        Class[] var2 = method.getParameterTypes();
        int var3 = var2.length;

        for (int var4 = 0; var4 < var3; ++var4) {
            Class<?> type = var2[var4];
            sb.append(type.getName()).append(",");
        }

        if (method.getParameterTypes().length > 0) {
            sb.delete(sb.length() - 1, sb.length());
        }

        sb.append(")");
        return sb.toString();
    }

    @Around("@annotation(com.zbkj.common.annotation.Cache)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String methodName = pjp.getSignature().getName();
        Class[] paramTypes = ((MethodSignature) pjp.getSignature()).getParameterTypes();
        Object[] args = pjp.getArgs();
        Method method = pjp.getTarget().getClass().getMethod(methodName, paramTypes);
        Cache cache = method.getAnnotation(Cache.class);
        String cacheKey = this.cacheKeys(cache.cacheKey(), method, args);
//        this.logger.info("cacheKey {}", cacheKey);
        boolean invokeProxy = true;

        try {
            Object ro = redisUtil.get(cacheKey);
            Object result;
            if (!ObjectUtil.isNull(ro)) {
                return ro;
            }
            result = pjp.proceed();
            redisUtil.set(cacheKey, result, Long.valueOf(cache.expireTime()));
            return result;
        } catch (Exception var26) {
            this.logger.error(var26.getMessage(), var26);
            if (invokeProxy) {
                Object var12 = pjp.proceed();
                return var12;
            } else {
                throw var26;
            }
        }
    }

    private String cacheKeys(String cacheKeys, Method method, Object[] args) {
        if (StringUtils.isBlank(cacheKeys)) {
            cacheKeys = getMethodLongName(method);
        }
        return generate(cacheKeys, args);
    }

    public int getOrder() {
        return 1;
    }

    private String generate(String key, Object[] args) {
        StringBuilder sb = new StringBuilder(key == null ? "" : key);
        List<Object> logArgs = ArrayUtils.isEmpty(args) ? Lists.newArrayList() : Arrays.stream(args)
                .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                .collect(Collectors.toList());
        sb.append(":").append(JSONObject.toJSONString(logArgs));

        logger.debug("generated key is {}", sb.toString());
        return sb.toString();
    }
}

